package cn.shadow.beans;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 默认的BeanFactory
 * @author 冯啸迪
 */
public class DefaultBeanFactory implements BeanFactory,BeanDefinitionRegister,Closeable {

    protected Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    private Map<String, Object> singletonBeanMap = new ConcurrentHashMap<>(256);

    private Map<Class<?>, Set<String>> typeMap = new ConcurrentHashMap<>(256);

    private ThreadLocal<Set<String>> buildingBeansRecordor = new ThreadLocal<>();

    private ThreadLocal<Map<String, Object>> earlyExposeBuildingBeans = new ThreadLocal<>();

    private List<BeanPostProcessor> beanPostProcessors =Collections.synchronizedList(new ArrayList<>());

/*
* ================================================================
* |                 BeanDefinitionRegister部分的实现
* |
* ================================================================
* */

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException {
        if (!beanDefinition.validate()){
            throw new BeanDefinitionRegistException("名字为[" + beanName + "] 的bean定义不合法：" + beanDefinition);
        }
        if (containsBeanDefinition(beanName)){
            throw new BeanDefinitionRegistException(beanName + "已经注册");
        }
        beanDefinitionMap.put(beanName, beanDefinition);
    }

    /**
     * 将Class和Bean进行整理
     */
    public void registerTypeMap() throws Exception {
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()){
            String beanName = entry.getKey();
            Class<?> type = this.getType(beanName);
            this.registerTypeMap(beanName, type);
            this.registerSuperTypeMap(beanName, type);
            this.registerInterfaceTypeMap(beanName, type);
        }
    }

    /**
     * 直接注册当前输入类
     * @param beanName 名称
     * @param clazz 类型
     */
    private void registerTypeMap(String beanName, Class<?> clazz){
        // 直接往里注册
        Set<String> beanNames = typeMap.get(clazz);
        if (beanNames == null){
            beanNames = new HashSet<>();
            typeMap.put(clazz, beanNames);
        }
        beanNames.add(beanName);

    }

    /**
     * 递归寻找父类，到Object或者为空的时候停止
     * @param beanName 名称
     * @param clazz 类型
     */
    private void registerSuperTypeMap(String beanName, Class<?> clazz){
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null && superClass.equals(Object.class)){
            this.registerTypeMap(beanName, superClass);
            this.registerSuperTypeMap(beanName, superClass);
            this.registerInterfaceTypeMap(beanName, superClass);
        }
    }

    /**
     * 递归注册接口
     * @param beanName Bean名称
     * @param clazz 接口类型
     */
    private void registerInterfaceTypeMap(String beanName, Class<?> clazz) {
        Class<?>[] interfaceClasses = clazz.getInterfaces();
        if (interfaceClasses.length >1){
            for (Class<?> interfaceClass : interfaceClasses){
                this.registerTypeMap(beanName, interfaceClass);
                this.registerInterfaceTypeMap(beanName, interfaceClass);
            }
        }
    }



    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        return beanDefinitionMap.get(beanName);
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return beanDefinitionMap.containsKey(beanName);
    }

/*
* =======================================================================
* |                         BeanFactory部分的实现
* |
* =======================================================================
*/

    @Override
    public void registerBeanPostProcess(BeanPostProcessor beanPostProcessor) {
        beanPostProcessors.add(beanPostProcessor);
        if (beanPostProcessor instanceof BeanFactoryAware){
            ((BeanFactoryAware) beanPostProcessor).setBeanFactory(this);
        }
    }

    @Override
    public Object getBean(String beanName) throws Throwable {
        return doGetBean(beanName);
    }

    @Override
    public <T> T getBean(Class<T> type) throws Throwable {
        // 1.可能存在多个类但是不同名称的，谁作为首选
        Set<String> beanNames = typeMap.get(type);
        if (beanNames != null){
            if (beanNames.size() == 1){
                // size等于1直接返回对应的对象
                return (T) this.getBean(beanNames.iterator().next());
            } else {
                // 一个类型存在多个BeanName
                String primaryName = null;
                for (String beanName : beanNames){
                    BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
                    if (beanDefinition != null && beanDefinition.isPrimary()){
                        if (primaryName != null){
                            // 之前已经声明过了
                            throw new Exception("该类型多个Bean为primary");
                        }
                        primaryName = beanName;
                    }
                }
                if (primaryName != null){
                    return (T) this.getBean(primaryName);
                } else {
                    throw new Exception(type + "类型没有设置primary");
                }
            }
        }
        return null;
    }

    /**
     * 去做获取Bean对象
     * 1.从单例缓存中获取数据，有返回对象，没有进入2
     * 1.5 通过缓存形式校验构造循环依赖
     * 2.从提前暴露的缓存中获取对象，有返回对象，没有进入3
     * 3.判断是否是单例，是单例进入4，不是单例进入5
     * 4.通过DCL进行单例构建
     * 5.直接构建新的Bean
     * 6.清空构造缓存
     * @param beanName bean名称
     * @return 返回Bean实例
     * @throws Exception 抛出异常
     */
    protected Object doGetBean(String beanName) throws Throwable {
        Objects.requireNonNull(beanName);
        // 从单例列表里边获取
        Object instance = singletonBeanMap.get(beanName);
        if (instance != null){
            return instance;
        }
        // 循环依赖检测
        Set<String> beanNames = buildingBeansRecordor.get();
        if (beanNames == null){
            beanNames = new HashSet<>();
            buildingBeansRecordor.set(beanNames);
        }
        if (beanNames.contains(beanName)){
            throw new Exception(beanNames + "出现循环依赖");
        }
        beanNames.add(beanName);
        // 从提前暴露的缓存中再次查找
        instance = getFromEarlyExposeBuildingBeans(beanName);
        if (instance != null){
            return instance;
        }
        BeanDefinition beanDefinition = getBeanDefinition(beanName);
        Objects.requireNonNull(beanDefinition);
        if (beanDefinition.isSingleton()){
            synchronized (this.singletonBeanMap){
                instance = singletonBeanMap.get(beanName);
                if (instance == null) {
                    instance = doCreateInstance(beanName, beanDefinition);
                    singletonBeanMap.put(beanName, instance);
                }
            }

        } else {
            instance = doCreateInstance(beanName, beanDefinition);
        }
        beanNames.remove(beanName);
        return instance;
    }

    /**
     * 实际的创建实例方法
     * @param bd 定义
     * @return 实例
     * @throws Exception 抛出异常
     */
    private Object doCreateInstance(String beanName, BeanDefinition bd) throws Throwable{
        Class<?> beanClass = bd.getBeanClass();
        Object instance = null;
        if (beanClass != null){

            if (StringUtils.isBlank(bd.getFactoryMethodName())){
                // beanClass + factoryMethodName 静态工厂
                instance = createInstanceByStaticFactoryMethod(bd);
            } else {
                // beanClass 构造器创建
                instance = createInstanceByConstructor(bd);
            }
        } else {
            // factoryBeanName + factoryMethodName 工厂实例
            instance = createInstanceByFactoryBean(bd);
        }
        // 对当前实例进行暴露，
        this.doEarlyExposeBuildingBeans(beanName,bd);
        // 对当前实例的属性进行赋值，也就是传说中的依赖注入
        this.setPropertyDIValues(bd, instance);
        // 将暴露的内容清空
        this.removeEarlyExposeBuildingBeans(beanName);
        // 初始化之前的操作
        instance = this.applyPostProcessorBeforeInitialization(instance, beanName);
        // 对象的初始化方法
        this.doInit(bd,instance);
        // 初始化之后的操作
        instance = this.applyPostProcessorAfterInitialization(instance, beanName);
        return instance;
    }

    /**
     * 在创建Bean的时候要提前进行暴露，放到单独的缓存里边
     * @param beanName 名称
     * @param instance 实例
     */
    private void doEarlyExposeBuildingBeans(String beanName, Object instance){
        Map<String, Object> expose = earlyExposeBuildingBeans.get();
        if (expose == null){
            expose = new HashMap<>();
            earlyExposeBuildingBeans.set(expose);
        }
        expose.put(beanName, instance);
    }

    /**
     * 完事之后还得把暴露的实力清空
     * @param beanName 名称
     */
    private void removeEarlyExposeBuildingBeans(String beanName){
        Map<String, Object> expose = earlyExposeBuildingBeans.get();
        expose.remove(beanName);
    }

    /**
     * 在提前暴露的缓存中查询数据
     * @param beanName 名称
     * @return 提前暴露的实例
     */
    private Object getFromEarlyExposeBuildingBeans(String beanName){
        Map<String, Object> expose = earlyExposeBuildingBeans.get();
        return expose.get(beanName);
    }

    /**
     * 进行依赖注入
     * @param beanDefinition Bean定义
     * @param instance 上个实例
     * @throws Exception 抛出的异常
     */
    private void setPropertyDIValues(BeanDefinition beanDefinition, Object instance) throws Throwable {
        // 获取Bean需要被注入的列表
        List<PropertyValue> propertyValues = beanDefinition.getPropertyValues();
        // 获取需要的实例的class
        Class<?> clazz = instance.getClass();
        for (PropertyValue pv : propertyValues){
            // 循环列表
            if (StringUtils.isBlank(pv.getFieldName())){
                continue;
            }
            // 根据名称获取属性
            Field field = clazz.getField(pv.getFieldName());
            // DI
            field.setAccessible(true);
            field.set(instance, this.getOneArgumentRealValue(pv.getFieldValue()));
        }
    }

    @Override
    public <T> Map<String, T> getBeansByType(Class<T> clazz) throws Throwable {
        Set<String> beanNames = typeMap.get(clazz);
        if (beanNames != null){

            Map<String, T> result = new HashMap<>();
            for (String name : beanNames){
                result.put(name, (T) this.getBean(name));
            }
            return result;
        }

        return null;
    }

    @Override
    public Class<?> getType(String beanName) throws Exception {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        Class<?> type = beanDefinition.getBeanClass();
        if (type != null){
            // 判断是否是工厂
            if (StringUtils.isNotBlank(beanDefinition.getFactoryMethodName())){
                type.getDeclaredMethod(beanDefinition.getFactoryMethodName(),null).getReturnType();
            }
        } else {
            type = this.getType(beanDefinition.getFactoryBeanName());
            type = type.getClass().getDeclaredMethod(beanDefinition.getFactoryMethodName(), null).getReturnType();
        }
        return type;
    }

    /**
     * 应用PostProcessor的初始化前置操作
     * @param instance 实例
     * @param beanName Bean名称
     * @return 返回对象
     * @throws Throwable 抛出异常
     */
    private Object applyPostProcessorBeforeInitialization(Object instance, String beanName) throws Throwable {
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors){
            instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
        }
        return instance;
    }

    /**
     * 应用PostProcessor的初始化后置操作
     * @param instance 实例
     * @param beanName Bean名称
     * @return 返回对象
     * @throws Throwable 抛出异常
     */
    private Object applyPostProcessorAfterInitialization(Object instance, String beanName) throws Throwable {
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors){
            instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
        }
        return instance;
    }

    @Override
    public <T> List<T> getBeansByTypeList(Class<T> type) throws Throwable {
        Set<String> beanNames = this.typeMap.get(type);
        if (CollectionUtils.isEmpty(beanNames)){
            return null;
        }
        List<T> beans = new ArrayList<>();
        for (String beanName : beanNames){
            beans.add((T) this.getBean(beanName));
        }
        return beans;
    }

    /*
* =========================================================================
*|                              创建实例
*|  1.根据构造方法创建实例
*|  2.静态工厂创建
*|  3.实例工厂创建
*|
* =========================================================================
* */



    /**
     * 创建实例的相关方法
     * 通过构造器常见
     * 通过工厂实例创建
     * 通过工厂方法创建
     * @param beanDefinition
     * @return
     * @throws Exception
     */
    private Object createInstanceByConstructor(BeanDefinition beanDefinition) throws Throwable {
        // 首先获取参数列表
        Object[] args = getConstructorArgumentValues(beanDefinition);
        return this.getEnableConstructor(beanDefinition,args).newInstance(args);
    }

    private Object createInstanceByFactoryBean(BeanDefinition beanDefinition) throws Throwable {
        // 先通过BeanName获取工厂注册对象
        Object factory = doGetBean(beanDefinition.getFactoryBeanName());
        Object[]args = this.getConstructorArgumentValues(beanDefinition);
        Method method = this.determineFactoryMethod(beanDefinition, args, this.getType(beanDefinition.getFactoryBeanName()));
        // 通过反射的形式调用工厂实例方法
        return method.invoke(factory, args);
    }

    private Object createInstanceByStaticFactoryMethod(BeanDefinition beanDefinition) throws Throwable {
        Class<?> type = beanDefinition.getBeanClass();
        Object[]args = this.getConstructorArgumentValues(beanDefinition);
        Method m = this.determineFactoryMethod(beanDefinition, args, type);
        return m.invoke(type, args);
    }


    /**
     * 获取实际的构造参数
     * @param beanDefinition Bean定义
     * @return 参数列表
     * @throws Exception 抛出异常
     */
    private Object[] getConstructorArgumentValues(BeanDefinition beanDefinition) throws Throwable{
        // 首先获取参数列表
        List<?> argsType = beanDefinition.getConstructorArgumentValues();
        if (CollectionUtils.isEmpty(argsType)){
            return null;
        }
        Object[] values = new Object[argsType.size()];
        for (int i = 0 ; i < values.length ; i++){
            Object obj = getOneArgumentRealValue(argsType.get(i));
            values[i] = obj;
        }
        return values;
    }

    /**
     * 获取一个参数参数的真实值
     * @param originalValue 可能是一个准确的常量，例如数字，字符串，也可能是一个被容器管理的对象，那就需要BeanReference包装一下
     * @return 返回真实值
     * @throws Exception 抛出异常
     */
    private Object getOneArgumentRealValue(Object originalValue) throws Throwable {
        Object realValue = null;
        if (originalValue != null){
            if (originalValue instanceof BeanReference){
                if (StringUtils.isNotBlank(((BeanReference) originalValue).getBeanName())){
                    realValue = this.getBean(((BeanReference) originalValue).getBeanName());
                }else if (originalValue instanceof Object[]) {
                    // TODO 处理集合中的bean引用
                } else if (originalValue instanceof Collection) {
                    // TODO 处理集合中的bean引用
                } else if (originalValue instanceof Properties) {
                    // TODO 处理properties中的bean引用
                } else if (originalValue instanceof Map){
                    // TODO 处理Map中的bean引用
                } else {
                    realValue = originalValue;
                }
            }
            // 可能还得有其他的内容
        }
        return realValue;
    }

    /**
     * 获取一个确定可用的构建参数
     * @param beanDefinition bean的定义
     * @param args 参数列表
     * @return 返回构造方法
     * @throws Exception 没找到抛出异常
     */
    private Constructor<?> getEnableConstructor(BeanDefinition beanDefinition, Object[] args) throws Exception {
        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?> constructor = null;
        if (args == null){
            return beanClass.getConstructor(null);
        }
        if (beanDefinition.getConstructor() != null){
            return beanClass.getConstructor();
        }
        Class<?>[] argsType = new Class[args.length];
        int j = 0;
        for (Object obj : args){
            argsType[j++] = obj.getClass();
        }
        // 这个是精准查找
        try {
            constructor = beanClass.getConstructor(argsType);
        } catch (Exception e){

        }
        // 模糊查找
        for (Constructor<?> ct : beanClass.getConstructors()){
            // 现根据参数数量进行对比
            if (ct.getParameters().length == args.length){
                Class<?>[] paramsType = ct.getParameterTypes();
                boolean flag = true;
                for (int i = 0 ; i < paramsType.length ; i++){
                    if (!paramsType[i].equals(args[i])){
                        flag = false;
                        break;
                    }
                }
                if (flag){
                    constructor = ct;
                    break;
                }
            }
        }

        if (constructor != null){
            return constructor;
        } else {
            throw new Exception("没有找到构造器");
        }

    }

    /**
     * 工厂部分获取方法
     * @param bd Bean的定义
     * @param args 参数类型列表
     * @param type 类型
     * @return 工厂用来构造Bean的方法
     */
    private Method determineFactoryMethod(BeanDefinition bd, Object[] args, Class<?> type) throws Exception {
        Method m = null;
        // 先从缓存里边找
        m = bd.getFactoryMethod();
        if (m != null){
            return m;
        }
        // 缓存里边没有，如果没有参数
        String methodName = bd.getFactoryMethodName();
        if (args == null){
            // 没有入参
            m = type.getMethod(methodName, null);
            bd.setFactoryMethod(m);
            return m;
        }
        // 精准查找
        Class<?>[] argsType = new Class[args.length];
        for (int i = 0 ; i < args.length ; i++){
            argsType[i] = args[i].getClass();
        }
        try {
            m = type.getMethod(methodName, argsType);
        } catch (Exception e){
            //
        }
        if (m == null){
            // 精准查找没找到
            for (Method method : type.getMethods()){
                // 获取所有方法
                if (!method.getName().equals(methodName)){
                    // 名字不匹配
                    continue;
                }
                if (method.getParameterTypes().length == argsType.length){
                    // 名字匹配后，一堆对照参数列表
                    boolean flag = true;
                    for (int i = 0 ; i < method.getParameterTypes().length ; i++){
                        if (!method.getParameterTypes()[i].equals(argsType[i])){
                            flag = false;
                            break;
                        }
                    }
                    if (flag){
                        m = method;
                        break;
                    }
                }
            }
        }

        if (m != null){
            bd.setFactoryMethod(m);
            return m;
        } else {
            throw new Exception("没有找到对应的方法");
        }
    }



    /**
     * 执行初始化方法
     *
     * @param beanDefinition Bean的定义需要从中获取到加载方法
     * @param instance 让具体的哪个对象来调用初始化方法
     * @throws Exception 抛出的异常
     */
    private void doInit(BeanDefinition beanDefinition, Object instance) throws Exception {
        if (beanDefinition.getInitMethodName() != null){
            Method method = instance.getClass().getMethod(beanDefinition.getInitMethodName());
            method.invoke(instance,null);
        }
    }

/*
* ======================================================================
*|                          Closeable接口实现
*|
*|
* ======================================================================
* */

    @Override
    public void close() throws IOException {
        // 关闭整个IOC
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()){
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            Object instance = singletonBeanMap.get(entry.getKey());
            if (instance != null){
                // 如果都不在单例缓存中的话也就没必要销毁了
                try {
                    Method method = instance.getClass().getMethod(beanDefinition.getDestroyMethodName(), null);
                    method.invoke(instance,null);
                } catch (NoSuchMethodException|InvocationTargetException|IllegalAccessException e) {
                    System.out.println("执行bean[" + beanName + "] " + beanDefinition + " 的 销毁方法异常！");
                    System.out.printf(e.getMessage());
                }


            }
        }


    }
}
